# 作者：詹广权
# 版权所有：www.cdsjcc.cn/su-xz
# 说明：线转槽可视化预览模块 - 使用官方API重构版
# 功能：提供线转槽的实时可视化预览工具，支持交互式凹槽创建。

require 'sketchup.rb'
require File.join(File.dirname(__FILE__), 'cs.rb')

module SuXZ
  module YL
    # 全局调试开关 - 默认关闭
    DEBUG_MODE = false
    
    # 键盘常量定义
    VK_LEFT = 37
    VK_UP = 38
    VK_RIGHT = 39
    VK_DOWN = 40
    VK_ESCAPE = 27
    VK_ENTER = 13
    VK_TAB = 9
    
    # 鼠标按键常量
    MK_LBUTTON = 0x0001  # 左键
    MK_RBUTTON = 0x0002  # 右键
    MK_MBUTTON = 0x0010  # 中键
    
    # 修饰键掩码 (SketchUp 2024 API)
    ALT_MODIFIER_MASK   = 0x0001  # Alt键
    CTRL_MODIFIER_MASK  = 0x0002  # Ctrl键
    SHIFT_MODIFIER_MASK = 0x0004  # Shift键
    
    # 常量定义
    Z_AXIS = Geom::Vector3d.new(0, 0, 1)
    
    # 获取设置
    def self.get_settings
      return SuXZ::CS.get_settings('KC')
    end
    
    # 切换调试模式
    def self.toggle_debug_mode
      # 反转调试模式状态
      const_set(:DEBUG_MODE, !DEBUG_MODE)
      # 输出新的状态
      puts "线转槽调试模式: #{DEBUG_MODE ? '已开启' : '已关闭'}"
      return DEBUG_MODE
    end
    
    # 保存设置
    def self.save_settings(depth, width, position, inward = true)
      # 确保深度和宽度是浮点数
      if depth.is_a?(String)
        depth = depth.to_f.mm
      end
      
      if width.is_a?(String)
        width = width.to_f.mm
      end
      
      settings = {
        'depth' => depth,
        'width' => width,
        'position' => position,
        'inward' => inward
      }
      puts "保存设置: 深度=#{depth}, 宽度=#{width}, 位置=#{position}, 朝向=#{inward ? '向内' : '向外'}"
      return SuXZ::CS.save_settings('KC', settings)
    end
    
    # 显示设置对话框
    def self.show_settings_dialog
      return SuXZ::CS.show_settings_dialog('KC')
    end
    
    # 线转槽工具类 - 使用官方API重构
    class GroovePreviewTool
      # 工具ID，用于状态跟踪
      ID = self.object_id.to_s
      
      # 边线捕捉距离（像素）- 值越大越容易捕捉到线
      EDGE_PICK_DISTANCE = 20  # 默认20像素
      
      def initialize
        # 获取当前设置
        @settings = YL.get_settings
        @model = Sketchup.active_model
        @view = @model.active_view
        
        # 状态跟踪
        @ip = Sketchup::InputPoint.new    # 用于精确选择
        @ip2 = Sketchup::InputPoint.new   # 用于存储上一个点的状态
        @selected_edge = nil              # 当前选中的边线
        @preview_valid = false            # 预览是否有效
        @hover_preview_valid = false      # 悬停预览是否有效
        @mouse_position = Geom::Point3d.new  # 当前鼠标位置
        @hovered_edge = nil               # 鼠标悬停时的边线
        @last_hovered_edge = nil          # 上一个悬停的边线
        
        # 用于组和组件内部的坐标转换
        @pick_path = nil                  # 当前选中边线的路径
        @hover_pick_path = nil            # 当前悬停边线的路径
        @transformation = nil             # 当前选中边线的变换矩阵
        @hover_transformation = nil       # 当前悬停边线的变换矩阵
        
        # 预览数据
        @preview_data = {
          points: [],
          lines: [],
          faces: []
        }
        
        # 悬停预览数据（与正式预览分开）
        @hover_preview_data = {
          points: [],
          lines: [],
          faces: []
        }
        
        # 初始化完成
        if DEBUG_MODE
          puts "线转槽工具初始化完成（已启用边线优先捕捉和悬停预览）"
        end
      end
      
      # 工具被激活时
      def activate
        # 初始设置
        reset_tool
        
        # 显示工具提示
        show_tool_help
      end
      
      # 显示工具帮助信息
      def show_tool_help
        # 只在状态栏显示提示信息，不显示对话框
        Sketchup.status_text = "鼠标移动到边线上自动预览凹槽，点击直接创建。可连续点击多条边线创建多个凹槽。按ESC退出，F5或空格键重置。"
      end
      
      # 工具取消激活时
      def deactivate(view)
        # 清除状态
        reset_tool
        view.invalidate
      end
      
      # 重置工具状态
      def reset_tool
        # 重置所有状态变量
        @selected_edge = nil
        @hovered_edge = nil
        @last_hovered_edge = nil
        @preview_valid = false
        @hover_preview_valid = false
        @preview_data = {points: [], lines: [], faces: []}
        @hover_preview_data = {points: [], lines: [], faces: []}
        # 清除拾取路径
        @pick_path = nil
        @hover_pick_path = nil
        @transformation = nil
        @hover_transformation = nil
        
        # 刷新视图
        @view.invalidate if @view
      end
      
      # 左键点击
      def onLButtonDown(flags, x, y, view)
        # 如果已有选择的边线，点击确认创建
        if @selected_edge && @preview_valid
          create_groove
          return true
        end
        
        # 如果有悬停的边线，直接创建凹槽
        if @hovered_edge && @hovered_edge.valid? && !@hovered_edge.faces.empty? && @hover_preview_valid
          # 临时设置选中的边线，用于create_groove方法
          @selected_edge = @hovered_edge
          @preview_valid = true
          @preview_data = @hover_preview_data.clone
          @pick_path = @hover_pick_path     # 复制悬停边线的路径
          @transformation = @hover_transformation  # 复制悬停边线的变换矩阵
          
          # 直接创建凹槽
          create_groove
          return true
        end
        
        # 如果没有悬停边线，尝试选择一条新的边线
        ph = view.pick_helper
        ph.do_pick(x, y, calculate_pick_distance(view), true)  # 使用更大的拾取范围，与移动捕捉保持一致
        
        # 检查是否选中了边线
        edge = ph.picked_edge
        if edge && edge.valid? && !edge.faces.empty?
          # 获取边线的完整拾取路径，以便于在组或组件内部创建凹槽
          @pick_path = nil
          @transformation = nil
          
          ph.count.times do |i|
            if ph.leaf_at(i) == edge
              @pick_path = ph.path_at(i)
              
              # 如果边线在组或组件内，计算变换矩阵
              if @pick_path && @pick_path.length > 1
                # 找出所有组和组件
                containers = @pick_path.select { |e| 
                  e.is_a?(Sketchup::Group) || e.is_a?(Sketchup::ComponentInstance)
                }
                
                if !containers.empty?
                  # 计算复合变换矩阵，考虑嵌套组
                  compound_transformation = Geom::Transformation.new
                  
                  # 按照从外到内的顺序应用变换（反转容器列表）
                  containers.reverse.each do |container|
                    if container.is_a?(Sketchup::Group) || container.is_a?(Sketchup::ComponentInstance)
                      # 先应用外层变换，再应用内层变换
                      compound_transformation = compound_transformation * container.transformation
                      
                      # 添加调试输出
                      if DEBUG_MODE
                        puts "容器ID: #{container.entityID}, 类型: #{container.class.name}"
                        puts "变换矩阵: #{container.transformation.to_a.inspect}"
                        puts "复合变换累计: #{compound_transformation.to_a.inspect}"
                      end
                    end
                  end
                  @transformation = compound_transformation
                  
                  if DEBUG_MODE
                    puts "选中边线: 最终变换矩阵计算完成"
                  end
                end
              end
              
              break
            end
          end
          
          # 选择这条边线
          @selected_edge = edge
          
          # 计算预览几何
          calculate_preview_geometry
          
          # 如果预览有效，直接创建凹槽
          if @preview_valid
            create_groove
            return true
          else
            # 如果预览计算失败，至少更新状态
            Sketchup.status_text = "无法在此边线上创建凹槽"
            return false
          end
        else
          # 未选中有效边线，提示用户
          Sketchup.status_text = "请选择一条有效的边线（必须附着在面上）"
          return false
        end
      end
      
      # 鼠标移动
      def onMouseMove(flags, x, y, view)
        # 常规鼠标移动处理
        @mouse_position.x, @mouse_position.y = x, y
        
        # 只在无选择状态下使用InputPoint，减少处理开销
        unless @selected_edge
          # 使用pick_helper优先检测边线
          ph = view.pick_helper
          # 添加第四个参数true，启用穿透群组和组件的功能
          ph.do_pick(x, y, calculate_pick_distance(view), true) # 使用更大的拾取范围，大幅提高捕捉灵敏度
          
          # 优先检查边线
          edge = ph.picked_edge
          if edge && edge.valid? && !edge.faces.empty?
            # 获取边线的完整拾取路径
            hover_pick_path = nil
            hover_transformation = nil
            
            ph.count.times do |i|
              if ph.leaf_at(i) == edge
                hover_pick_path = ph.path_at(i)
                
                # 如果边线在组或组件内，计算变换矩阵
                if hover_pick_path && hover_pick_path.length > 1
                  # 找出所有组和组件
                  containers = hover_pick_path.select { |e| 
                    e.is_a?(Sketchup::Group) || e.is_a?(Sketchup::ComponentInstance)
                  }
                  
                  if !containers.empty?
                    # 计算复合变换矩阵，考虑嵌套组
                    compound_transformation = Geom::Transformation.new
                    
                    # 按照从外到内的顺序应用变换（反转容器列表）
                    # 这确保变换顺序正确：外部容器的变换先应用，然后是内部容器
                    containers.reverse.each do |container|
                      if container.is_a?(Sketchup::Group) || container.is_a?(Sketchup::ComponentInstance)
                        # 先应用外层变换，再应用内层变换
                        compound_transformation = compound_transformation * container.transformation
                        
                        # 添加调试输出
                        if DEBUG_MODE
                          puts "容器ID: #{container.entityID}, 类型: #{container.class.name}"
                          puts "变换矩阵: #{container.transformation.to_a.inspect}"
                          puts "复合变换累计: #{compound_transformation.to_a.inspect}"
                        end
                      end
                    end
                    hover_transformation = compound_transformation
                    
                    # 添加调试输出
                    if DEBUG_MODE
                      puts "最终变换矩阵: #{hover_transformation.to_a.inspect}"
                      puts "边线起点原始坐标: #{edge.start.position.to_s}"
                      puts "边线起点变换后坐标: #{(hover_transformation * edge.start.position).to_s}"
                    end
                  end
                end
                
                break
              end
            end
            @hover_pick_path = hover_pick_path
            @hover_transformation = hover_transformation
            
            # 如果悬停的边线改变，或之前没有悬停边线
            if @hovered_edge != edge || @last_hovered_edge != edge
              @hovered_edge = edge
              # 计算悬停预览
              calculate_hover_preview_geometry(edge)
              @last_hovered_edge = edge
            end
            
            # 直接将InputPoint设置在拾取的边线上
            if edge.start.position.distance(@ip.position) > edge.end.position.distance(@ip.position)
              ip_edge = edge.end
            else
              ip_edge = edge.start
            end
            @ip2.copy!(@ip)
            @ip.pick(view, x, y, @ip2)
            
            # 设置提示信息 - 添加组内信息
            tooltip_text = "边线: #{@hovered_edge.persistent_id}"
            if hover_pick_path && hover_pick_path.length > 1
              container_names = hover_pick_path[0...-1].map { |e| 
                e.is_a?(Sketchup::Group) ? "群组" : (e.is_a?(Sketchup::ComponentInstance) ? "组件" : e.class.name)
              }.join(" > ")
              tooltip_text += " (在#{container_names}内)"
            end
            tooltip_text += " (点击直接创建凹槽)"
            view.tooltip = tooltip_text
          else
            # 如果鼠标移出边线，清除悬停状态
            if @hovered_edge
              @hovered_edge = nil
              @hover_preview_valid = false
              @hover_preview_data = {points: [], lines: [], faces: []}
              @hover_pick_path = nil
              @hover_transformation = nil
            end
            
            # 标准点捕捉，但使用边线优先级
            @ip.pick(view, x, y, @ip2)
            view.tooltip = @ip.tooltip if @ip.valid?
          end
          
          # 更新视图以显示预览
          view.invalidate
        end
        
        # 始终返回false以允许其他视图操作
        return false
      end
      
      # 键盘事件处理
      def onKeyDown(flags, key, view, repeat = nil)
        # 退出工具
        if key == VK_ESCAPE || key == 27 # ESC
          if DEBUG_MODE
            puts "ESC键按下，退出工具"
          end
          # 重置工具
          reset_tool
          
          # 明确退出工具，切换回选择工具
          model = Sketchup.active_model
          if model
            model.select_tool(nil) # 切换回SketchUp默认选择工具
            Sketchup.status_text = "线转槽工具已退出，可以自由移动视图"
          end
          
          # 确保视图刷新
          view.invalidate if view
          return true
        end
        
        # F5或空格键：强制重置工具状态
        if key == 116 || key == 32 # F5或空格键
          if DEBUG_MODE
            puts "F5或空格键按下，强制重置工具状态"
          end
          force_reset_tool
          Sketchup.status_text = "工具状态已重置，可以继续操作"
          return true
        end
        
        # 对于所有其他按键，返回false让SketchUp处理默认行为
        return false
      end
      
      # 绘制预览
      def draw(view)
        # 清除视图状态 - 这可以帮助消除隐约可见的文字
        view.drawing_color = Sketchup::Color.new(0, 0, 0, 0)
        view.line_width = 1
        
        # 绘制左上角的参数显示（始终显示）
        draw_preview_parameters(view)
        
        # 已选中边线的预览处理
        if @selected_edge && @selected_edge.valid? && @preview_valid && !@preview_data[:faces].empty?
          # 绘制已选中边线的预览
          draw_groove_preview(view, @preview_data, @selected_edge)
          return
        end
        
        # 悬停预览处理 - 如果有悬停边线且预览有效
        if @hovered_edge && @hovered_edge.valid? && @hover_preview_valid && !@hover_preview_data[:faces].empty?
          # 绘制悬停边线预览 - 使用悬停专用的半透明效果
          draw_groove_preview(view, @hover_preview_data, @hovered_edge, true)
          return
        end
        
        # 如果没有预览，只绘制基本边线高亮
        if @hovered_edge && @hovered_edge.valid? && !@selected_edge
          view.drawing_color = Sketchup::Color.new(255, 220, 0, 200) # 黄色醒目提示
          view.line_width = 4
          
          # 如果在组或组件内，需要应用变换矩阵
          if @hover_transformation && !@hover_transformation.identity?
            # 转换边线端点到全局坐标系
            start_point = @hover_transformation * @hovered_edge.start.position
            end_point = @hover_transformation * @hovered_edge.end.position
            view.draw_line(start_point, end_point)
            
            # 绘制边线捕捉点
            view.drawing_color = Sketchup::Color.new(0, 255, 0, 220) # 绿色端点
            radius = 3
            view.draw_points([start_point, end_point], radius, 2, 'X')
          else
            # 直接绘制
            view.draw_line(@hovered_edge.start.position, @hovered_edge.end.position)
            
            # 绘制边线捕捉点
            view.drawing_color = Sketchup::Color.new(0, 255, 0, 220) # 绿色端点
            radius = 3
            view.draw_points([@hovered_edge.start.position, @hovered_edge.end.position], radius, 2, 'X')
          end
        end
        
        # 未选中边线时，只显示输入点帮助
        @ip.draw(view) if @ip.valid?
      end
      
      # 辅助方法：绘制凹槽预览（用于选中和悬停状态）
      def draw_groove_preview(view, preview_data, edge, is_hover = false)
        # 根据是否为悬停状态调整透明度
        alpha_fill = is_hover ? 60 : 80
        alpha_line = is_hover ? 140 : 160
        alpha_edge = is_hover ? 180 : 200
        
        # 获取正确的变换矩阵
        transformation = is_hover ? @hover_transformation : @transformation
        
        # 半透明填充
        view.drawing_color = Sketchup::Color.new(0, 120, 255, alpha_fill)
        preview_data[:faces].each do |face|
          # 如果有变换矩阵且非单位矩阵，转换坐标到全局坐标系用于绘制
          if transformation && !transformation.identity?
            # 转换为全局坐标
            global_face = face.map { |pt| transformation * pt }
            view.draw(GL_QUADS, global_face) # 使用GL_QUADS一次性绘制面
          else
          view.draw(GL_QUADS, face) # 使用GL_QUADS一次性绘制面
          end
        end
        
        # 轮廓线
        view.drawing_color = Sketchup::Color.new(0, 60, 220, alpha_line)
        view.line_width = 2
        preview_data[:faces].each do |face|
          if transformation && !transformation.identity?
            # 转换为全局坐标
            global_face = face.map { |pt| transformation * pt }
            view.draw(GL_LINE_LOOP, global_face)
          else
          view.draw(GL_LINE_LOOP, face)
          end
        end
        
        # 边线突出显示
        view.drawing_color = Sketchup::Color.new(255, 160, 0, alpha_edge)
        view.line_width = 3
        view.draw_line(edge.start.position, edge.end.position)
        
        # 恢复默认设置
        view.line_width = 1
        view.drawing_color = Sketchup::Color.new(0, 0, 0)
        
        # 仅在必要时绘制尺寸标注 - 但不绘制深度标注，避免与左上角面板重复
        draw_dimensions(view, false) if !is_hover
      end
      
      # 添加左上角显示预览参数的方法 - 完全重写版本（优化版）
      def draw_preview_parameters(view)
        # 设置显示位置 - 左上角，但下移一些避免可能的API生成文字
        left_margin = 10
        top_margin = 50  # 从10改为50，下移40像素
        
        # 计算背景尺寸和位置 - 增大以适应更大的字体
        bg_width = 220   # 从180增加到220
        bg_height = 70   # 从50增加到70以容纳更大字体
        
        # 绘制背景矩形 - 使用完全不透明背景确保覆盖任何后面的内容
        bg_points = [
          [left_margin, top_margin],  # 左上角
          [left_margin + bg_width, top_margin],  # 右上角
          [left_margin + bg_width, top_margin + bg_height],  # 右下角
          [left_margin, top_margin + bg_height]  # 左下角
        ]
        
        # 绘制完全不透明的背景
        view.drawing_color = Sketchup::Color.new(240, 240, 240, 255)  # 完全不透明
        view.draw2d(GL_QUADS, bg_points)
        
        # 绘制边框
        view.drawing_color = Sketchup::Color.new(80, 80, 80)
        view.line_width = 1
        view.draw2d(GL_LINE_LOOP, bg_points)
        
        # 绘制标题分割线 - 位置也相应调整
        title_line_y = top_margin + 35 # 调整分隔线位置适应更大的背景
        view.drawing_color = Sketchup::Color.new(180, 180, 180)
        view.draw2d(GL_LINES, [
          [left_margin + 5, title_line_y],
          [left_margin + bg_width - 5, title_line_y]
        ])
        
        # 实现更大字体效果的方法 - 由于无法直接设置字体大小，我们使用重叠绘制
        def draw_bigger_text(view, position, text, factor = 1.4)
          x, y = position
          
          # 基本字体
          view.draw_text([x, y], text)
          
          # 绘制加粗效果，但间距更大以实现更大字体效果
          step = (factor * 0.5).to_i
          [-step, step].each do |dx|
            [-step, step].each do |dy|
              next if dx.abs + dy.abs < step # 避免太多重叠
              view.draw_text([x + dx, y + dy], text)
            end
          end
        end
        
        # 标题文本 - 使用深蓝色，确保在分隔线上方，并增大
        view.drawing_color = Sketchup::Color.new(0, 60, 120)
        title_text = "预览：深度 #{@settings['depth']}"
        draw_bigger_text(view, [left_margin + 12, top_margin + 20], title_text)
        
        # 参数文本 - 使用深灰色，确保在分隔线下方，并增大
        view.drawing_color = Sketchup::Color.new(60, 60, 60)
        
        # 获取当前位置文本 - 这里需要添加注释以说明方向已修正
        position_text = @settings['position']
        # 添加"(已修正)"注释以提醒用户界面和实际效果现在一致
        params_text = "宽度 #{@settings['width']} - #{position_text} #{@settings['inward'] ? '向内' : '向外'}"
        draw_bigger_text(view, [left_margin + 12, top_margin + 50], params_text)
        
        # 恢复默认线宽
        view.line_width = 1
      end
      
      # 绘制尺寸标注 - 优化版，支持选择性显示深度标注
      def draw_dimensions(view, show_depth = true)
        # 只有在有效预览数据存在时才绘制
        return unless @preview_valid && @preview_data[:faces].size >= 2
        
        # 获取坐标转换矩阵
        transformation = @transformation
        
        # 使用缓存点，避免每帧重新计算
        # 深度标注 (红色) - 仅在需要时显示
        if show_depth
          view.drawing_color = Sketchup::Color.new(220, 0, 0, 180)
          view.line_stipple = "-"
          
          p1 = @preview_data[:faces][0][0]
          p2 = @preview_data[:faces][1][0]
          
          # 转换为全局坐标（如果需要）
          if transformation && !transformation.identity?
            p1 = transformation * p1
            p2 = transformation * p2
          end
          
          # 一次性绘制线段和文本
          view.draw_line(p1, p2)
          mid = Geom.linear_combination(0.5, p1, 0.5, p2)  # 更高效的中点计算
          view.draw_text(mid, "深度: #{@settings['depth']}")
        end
        
        # 宽度标注 (绿色) - 始终显示
        if @preview_data[:faces][0].size >= 4
          view.drawing_color = Sketchup::Color.new(0, 180, 0, 180)
          view.line_stipple = "-"
          
          p1 = @preview_data[:faces][0][0]
          p2 = @preview_data[:faces][0][3]
          
          # 转换为全局坐标（如果需要）
          if transformation && !transformation.identity?
            p1 = transformation * p1
            p2 = transformation * p2
          end
          
          view.draw_line(p1, p2)
          mid = Geom.linear_combination(0.5, p1, 0.5, p2)
          view.draw_text(mid, "宽度: #{@settings['width']}")
        end
        
        # 重置线型
        view.line_stipple = ""
      end
      
      # 强制重置工具状态 - 用于修复可能的拾取和显示问题
      def force_reset_tool
        # 完全清除所有状态
        @selected_edge = nil
        @hovered_edge = nil
        @last_hovered_edge = nil
        @preview_valid = false
        @hover_preview_valid = false
        @preview_data = {points: [], lines: [], faces: []}
        @hover_preview_data = {points: [], lines: [], faces: []}
        @pick_path = nil
        @hover_pick_path = nil
        @transformation = nil
        @hover_transformation = nil
        
        # 重新初始化输入点
        @ip = Sketchup::InputPoint.new
        @ip2 = Sketchup::InputPoint.new
        
        # 强制刷新视图
        if @view
          @view.invalidate
          if DEBUG_MODE
            puts "工具状态已强制重置，所有拾取和预览数据已清除"
          end
        end
      end
      
      # 计算预览几何数据
      def calculate_preview_geometry
        return unless @selected_edge && @selected_edge.valid?
        
        # 重置预览数据
        @preview_data = {points: [], lines: [], faces: []}
        @preview_valid = false
        
        # 获取相关几何数据
        edge = @selected_edge
        face = edge.faces.first
        
        # 如果找不到面，则无法创建凹槽
        unless face && face.valid?
          puts "错误：边线未附着在有效面上"
          return
        end
        
        # 方向和朝向计算
        edge_vector = edge.line[1].normalize
        normal_vector = face.normal
        offset_vector = normal_vector * edge_vector
        
        # 创建面的法向量（用于深度方向）
        depth_vector = Geom::Vector3d.new(0, 0, -1)
        
        # 如果在组或组件内，需要调整深度向量方向
        if @transformation && !@transformation.identity?
          # 转换Z轴方向到组内坐标系
          depth_vector = @transformation.inverse * depth_vector
        end
        
        # 确保深度是浮点数
        depth = @settings['depth']
        if depth.is_a?(String)
          depth = depth.to_f.mm
        end
        depth_vector.length = depth
        
        # 根据朝向和位置设置偏移矢量
        inward = @settings['inward']
        
        case @settings['position']
        when "右边"
          # 确保宽度是浮点数
          width = @settings['width']
          if width.is_a?(String)
            width = width.to_f.mm
          end
          # 设置偏移矢量长度为凹槽宽度
          offset_vector.length = width
          
          # 检查朝向并根据需要翻转矢量
          if !inward
            offset_vector.reverse!
          end
          
          # 创建凹槽路径的点 - 在边线实际所在的坐标系中工作
          # 提取边线端点 - 这些已经在正确的坐标系统中
          start_point = edge.start.position.clone
          end_point = edge.end.position.clone
          
          # 在边线所在的坐标系中创建偏移点
          offset_start = start_point.offset(offset_vector)
          offset_end = end_point.offset(offset_vector)
          
          # 顶面四个点
          top_points = [
            start_point,
            end_point,
            offset_end,
            offset_start
          ]
          
          # 底面四个点 (向下偏移depth)
          bottom_points = top_points.map { |p| p.offset(depth_vector) }
          
          # 存储顶面和底面
          @preview_data[:faces] << top_points
          @preview_data[:faces] << bottom_points
          
          # 侧面
          @preview_data[:faces] << [top_points[0], top_points[1], bottom_points[1], bottom_points[0]] # 前面
          @preview_data[:faces] << [top_points[1], top_points[2], bottom_points[2], bottom_points[1]] # 右面
          @preview_data[:faces] << [top_points[2], top_points[3], bottom_points[3], bottom_points[2]] # 后面
          @preview_data[:faces] << [top_points[3], top_points[0], bottom_points[0], bottom_points[3]] # 左面
          
        when "左边"
          # 确保宽度是浮点数
          width = @settings['width']
          if width.is_a?(String)
            width = width.to_f.mm
          end
          # 与左边逻辑类似，但朝向相反
          offset_vector.length = width
          
          # 对于左边，需要翻转一次
          offset_vector.reverse!
          
          # 如果设置为向外，则再次翻转
          if !inward
            offset_vector.reverse!
          end
          
          # 提取边线端点 - 这些已经在正确的坐标系统中
          start_point = edge.start.position.clone
          end_point = edge.end.position.clone
          
          # 在边线所在的坐标系中创建偏移点
          offset_start = start_point.offset(offset_vector)
          offset_end = end_point.offset(offset_vector)
          
          # 与左边相同的逻辑构建面
          top_points = [
            start_point,
            end_point,
            offset_end,
            offset_start
          ]
          
          bottom_points = top_points.map { |p| p.offset(depth_vector) }
          
          @preview_data[:faces] << top_points
          @preview_data[:faces] << bottom_points
          
          # 侧面
          @preview_data[:faces] << [top_points[0], top_points[1], bottom_points[1], bottom_points[0]]
          @preview_data[:faces] << [top_points[1], top_points[2], bottom_points[2], bottom_points[1]]
          @preview_data[:faces] << [top_points[2], top_points[3], bottom_points[3], bottom_points[2]]
          @preview_data[:faces] << [top_points[3], top_points[0], bottom_points[0], bottom_points[3]]
          
        when "中间"
          # 确保宽度是浮点数
          width = @settings['width']
          if width.is_a?(String)
            width = width.to_f.mm
          end
          # 中间位置：槽在边线两侧
          half_width = width / 2.0
          offset_vector.length = half_width
          
          # 根据向内/向外设置方向
          if !inward
            offset_vector.reverse!
          end
          
          # 提取边线端点 - 这些已经在正确的坐标系统中
          start_point = edge.start.position.clone
          end_point = edge.end.position.clone
          
          # 创建一侧的偏移点
          offset_start1 = start_point.offset(offset_vector)
          offset_end1 = end_point.offset(offset_vector)
          
          # 另一侧偏移点
          offset_vector.reverse!
          offset_start2 = start_point.offset(offset_vector)
          offset_end2 = end_point.offset(offset_vector)
          
          # 顶面四个点
          top_points = [
            offset_start2,
            offset_end2,
            offset_end1,
            offset_start1
          ]
          
          # 底面四个点
          bottom_points = top_points.map { |p| p.offset(depth_vector) }
          
          @preview_data[:faces] << top_points
          @preview_data[:faces] << bottom_points
          
          # 侧面
          @preview_data[:faces] << [top_points[0], top_points[1], bottom_points[1], bottom_points[0]]
          @preview_data[:faces] << [top_points[1], top_points[2], bottom_points[2], bottom_points[1]]
          @preview_data[:faces] << [top_points[2], top_points[3], bottom_points[3], bottom_points[2]]
          @preview_data[:faces] << [top_points[3], top_points[0], bottom_points[0], bottom_points[3]]
        end
        
        # 预览数据创建成功
        @preview_valid = true
      end
      
      # 计算悬停预览几何数据 - 与calculate_preview_geometry类似，但针对悬停状态
      def calculate_hover_preview_geometry(edge)
        return unless edge && edge.valid?
        
        # 重置悬停预览数据
        @hover_preview_data = {points: [], lines: [], faces: []}
        @hover_preview_valid = false
        
        # 获取相关几何数据
        face = edge.faces.first
        
        # 如果找不到面，则无法创建凹槽
        unless face && face.valid?
          return
        end
        
        # 方向和朝向计算
        edge_vector = edge.line[1].normalize
        normal_vector = face.normal
        offset_vector = normal_vector * edge_vector
        
        # 创建面的法向量（用于深度方向）
        depth_vector = Geom::Vector3d.new(0, 0, -1)
        
        # 如果在组或组件内，需要调整深度向量方向
        if @hover_transformation && !@hover_transformation.identity?
          # 转换Z轴方向到组内坐标系
          depth_vector = @hover_transformation.inverse * depth_vector
        end
        
        # 确保深度是浮点数
        depth = @settings['depth']
        if depth.is_a?(String)
          depth = depth.to_f.mm
        end
        depth_vector.length = depth
        
        # 根据朝向和位置设置偏移矢量
        inward = @settings['inward']
        
        case @settings['position']
        when "右边"
          # 确保宽度是浮点数
          width = @settings['width']
          if width.is_a?(String)
            width = width.to_f.mm
          end
          # 设置偏移矢量长度为凹槽宽度
          offset_vector.length = width
          
          # 检查朝向并根据需要翻转矢量
          if !inward
            offset_vector.reverse!
          end
          
          # 提取边线端点 - 这些已经在正确的坐标系统中
          start_point = edge.start.position.clone
          end_point = edge.end.position.clone
          
          # 在边线所在的坐标系中创建偏移点
          offset_start = start_point.offset(offset_vector)
          offset_end = end_point.offset(offset_vector)
          
          # 顶面四个点
          top_points = [
            start_point,
            end_point,
            offset_end,
            offset_start
          ]
          
          # 底面四个点 (向下偏移depth)
          bottom_points = top_points.map { |p| p.offset(depth_vector) }
          
          # 存储顶面和底面
          @hover_preview_data[:faces] << top_points
          @hover_preview_data[:faces] << bottom_points
          
          # 侧面
          @hover_preview_data[:faces] << [top_points[0], top_points[1], bottom_points[1], bottom_points[0]] # 前面
          @hover_preview_data[:faces] << [top_points[1], top_points[2], bottom_points[2], bottom_points[1]] # 右面
          @hover_preview_data[:faces] << [top_points[2], top_points[3], bottom_points[3], bottom_points[2]] # 后面
          @hover_preview_data[:faces] << [top_points[3], top_points[0], bottom_points[0], bottom_points[3]] # 左面
          
        when "左边"
          # 确保宽度是浮点数
          width = @settings['width']
          if width.is_a?(String)
            width = width.to_f.mm
          end
          # 与左边逻辑类似，但朝向相反
          offset_vector.length = width
          
          # 对于左边，需要翻转一次
          offset_vector.reverse!
          
          # 如果设置为向外，则再次翻转
          if !inward
            offset_vector.reverse!
          end
          
          # 提取边线端点 - 这些已经在正确的坐标系统中
          start_point = edge.start.position.clone
          end_point = edge.end.position.clone
          
          # 在边线所在的坐标系中创建偏移点
          offset_start = start_point.offset(offset_vector)
          offset_end = end_point.offset(offset_vector)
          
          # 与左边相同的逻辑构建面
          top_points = [
            start_point,
            end_point,
            offset_end,
            offset_start
          ]
          
          bottom_points = top_points.map { |p| p.offset(depth_vector) }
          
          @hover_preview_data[:faces] << top_points
          @hover_preview_data[:faces] << bottom_points
          
          # 侧面
          @hover_preview_data[:faces] << [top_points[0], top_points[1], bottom_points[1], bottom_points[0]]
          @hover_preview_data[:faces] << [top_points[1], top_points[2], bottom_points[2], bottom_points[1]]
          @hover_preview_data[:faces] << [top_points[2], top_points[3], bottom_points[3], bottom_points[2]]
          @hover_preview_data[:faces] << [top_points[3], top_points[0], bottom_points[0], bottom_points[3]]
          
        when "中间"
          # 确保宽度是浮点数
          width = @settings['width']
          if width.is_a?(String)
            width = width.to_f.mm
          end
          # 中间位置：槽在边线两侧
          half_width = width / 2.0
          offset_vector.length = half_width
          
          # 根据向内/向外设置方向
          if !inward
            offset_vector.reverse!
          end
          
          # 提取边线端点 - 这些已经在正确的坐标系统中
          start_point = edge.start.position.clone
          end_point = edge.end.position.clone
          
          # 创建一侧的偏移点
          offset_start1 = start_point.offset(offset_vector)
          offset_end1 = end_point.offset(offset_vector)
          
          # 另一侧偏移点
          offset_vector.reverse!
          offset_start2 = start_point.offset(offset_vector)
          offset_end2 = end_point.offset(offset_vector)
          
          # 顶面四个点
          top_points = [
            offset_start2,
            offset_end2,
            offset_end1,
            offset_start1
          ]
          
          # 底面四个点
          bottom_points = top_points.map { |p| p.offset(depth_vector) }
          
          @hover_preview_data[:faces] << top_points
          @hover_preview_data[:faces] << bottom_points
          
          # 侧面
          @hover_preview_data[:faces] << [top_points[0], top_points[1], bottom_points[1], bottom_points[0]]
          @hover_preview_data[:faces] << [top_points[1], top_points[2], bottom_points[2], bottom_points[1]]
          @hover_preview_data[:faces] << [top_points[2], top_points[3], bottom_points[3], bottom_points[2]]
          @hover_preview_data[:faces] << [top_points[3], top_points[0], bottom_points[0], bottom_points[3]]
        end
        
        # 悬停预览数据创建成功
        @hover_preview_valid = true
      end
      
      # 创建凹槽
      def create_groove
        return unless @selected_edge && @preview_valid
        
        begin
          # 使用KC模块创建实际凹槽
          if defined?(SuXZ::KC)
            # 保存当前的设置到模块
            YL.save_settings(@settings['depth'], @settings['width'], @settings['position'], @settings['inward'])
            
            # 显示创建进度
            Sketchup.status_text = "正在创建凹槽..."
            
            # 创建凹槽 - 直接传递边线对象和拾取路径，而不是通过选择集
            result = SuXZ::KC.create_groove_directly(
              @selected_edge,
              @settings['depth'], 
              @settings['width'], 
              @settings['position'],
              @settings['inward'],
              @pick_path  # 传递拾取路径
            )
            
            # 反馈结果
            if result
              path_info = ""
              if @pick_path && @pick_path.length > 1
                containers = @pick_path[0...-1].map { |e| 
                  e.is_a?(Sketchup::Group) ? "群组" : (e.is_a?(Sketchup::ComponentInstance) ? "组件" : e.class.name)
                }
                path_info = "（在#{containers.join(' > ')}内）"
              end
              Sketchup.status_text = "凹槽创建成功！#{path_info}继续移动鼠标到其他边线可以创建更多凹槽..."
            else
              Sketchup.status_text = "凹槽创建失败，请检查选择和设置"
            end
          else
            UI.messagebox("错误：找不到凹槽创建模块！")
          end
        rescue => e
          if DEBUG_MODE
            puts "创建凹槽时出错: #{e.message}\n#{e.backtrace.join("\n")}"
          end
          UI.messagebox("创建凹槽时出错: #{e.message}")
        ensure
          # 仅重置工具状态，但不退出工具
          reset_tool_state_for_continuous_creation
          
          # 刷新视图
          @view.invalidate if @view
        end
      end
      
      # 重置工具状态用于连续创建
      def reset_tool_state_for_continuous_creation
        # 只重置必要的状态变量，保持工具处于活跃状态
        @selected_edge = nil
        @hovered_edge = nil
        @last_hovered_edge = nil
        @preview_valid = false
        @hover_preview_valid = false
        @preview_data = {points: [], lines: [], faces: []}
        @hover_preview_data = {points: [], lines: [], faces: []}
        # 清除拾取路径和变换矩阵
        @pick_path = nil
        @hover_pick_path = nil
        @transformation = nil
        @hover_transformation = nil
      end
      
      # 游标设置
      def onSetCursor
        # 如果已选择边线，显示"创建"游标
        if @selected_edge
          ui_id = 2 # 十字形
        else
          ui_id = 1 # 标准光标
        end
        UI.set_cursor(ui_id)
      end
      
      # 获取状态文本
      def get_status_string
        if @selected_edge
          "当前设置: 深度=#{@settings['depth']}, 宽度=#{@settings['width']}, 位置=#{@settings['position']}, 朝向=#{@settings['inward'] ? '向内' : '向外'}"
        elsif @hovered_edge && @hover_preview_valid
          "移动到边线上自动预览 | 点击直接创建凹槽 | 可连续创建多个凹槽"
        else
          "移动鼠标到边线上预览凹槽效果 | 可连续点击多条边线"
        end
      end
      
      # 处理双击事件，用于SketchUp标准缩放行为
      def onLButtonDoubleClick(flags, x, y, view)
        # 返回false以允许SketchUp的默认双击行为（通常是缩放到所选对象）
        Sketchup.status_text = "允许视图导航..."
        return false
      end
      
      # 处理鼠标滚轮事件，用于SketchUp标准滚轮缩放
      def onMouseWheel(flags, delta, x, y, view)
        # 返回false以允许SketchUp的默认滚轮行为（通常是缩放视图）
        return false
      end
      
      # 处理右键按下事件，支持右键菜单和视图导航
      def onRButtonDown(flags, x, y, view)
        # 返回false以允许默认右键行为
        return false
      end
      
      # 处理右键释放事件
      def onRButtonUp(flags, x, y, view)
        # 返回false以允许默认右键行为
        return false
      end
      
      # 根据视图缩放计算实际拾取距离
      def calculate_pick_distance(view)
        # 基础拾取距离
        base_distance = EDGE_PICK_DISTANCE
        
        # 获取当前视图缩放级别
        zoom_factor = 1.0
        begin
          # 尝试获取视图缩放因子
          camera = view.camera
          eye = camera.eye
          target = camera.target
          
          # 计算视点到目标的距离
          distance = eye.distance(target)
          
          # 调整拾取距离，距离越远，拾取范围越大
          if distance > 0
            zoom_factor = [1.0, distance / 100.0].max
            zoom_factor = [zoom_factor, 3.0].min  # 限制最大值
          end
        rescue => e
          if DEBUG_MODE
            puts "计算视图缩放因子时出错: #{e.message}"
          end
          zoom_factor = 1.0  # 出错时使用默认值
        end
        
        # 返回调整后的拾取距离
        adjusted_distance = (base_distance * zoom_factor).to_i
        if DEBUG_MODE
          puts "当前拾取距离: #{adjusted_distance}像素 (缩放因子: #{zoom_factor.round(2)})"
        end
        return adjusted_distance
      end
    end
    
    # 启动凹槽预览工具
    def self.start_preview_tool
      # 获取当前设置
      settings = self.get_settings
      
      # 创建并激活工具
      tool = GroovePreviewTool.new
      Sketchup.active_model.select_tool(tool)
      
      if DEBUG_MODE
        puts "线转槽工具已启动"
      end
      
      return tool
    end
    
    # 初始化
    def self.init
      puts "线转槽可视化预览模块已加载"
    end
    
    # 安全退出工具的方法 - 可从任何地方调用
    def self.exit_tool
      model = Sketchup.active_model
      if model
        # 切换回默认选择工具
        model.select_tool(nil)
        Sketchup.status_text = "线转槽工具已退出，可以自由移动视图"
        
        # 刷新视图
        model.active_view.invalidate if model.active_view
        
        if DEBUG_MODE
          puts "线转槽工具已安全退出"
        end
        return true
      end
      return false
    end
  end
  
  # 初始化YL模块
  YL.init
end 